///////////////////////////////////////////////////////////////////////////////////////////////////
// Illusion Ray Development Kit (IDK)           Copyright (c) 2010 ( dimitriy.martynenko@gmail.com )
///////////////////////////////////////////////////////////////////////////////////////////////////
// Created : 2010-05-22
// Updated : ...
// Licence : NEW BSD
// Info	   : "SCL" Safe Code Lib (for C++ unmanaged)
///////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------------------------------------------
| TString:
| ------------
| Represents text as a series of Unicode characters.
---------------------------------------------------------------------------*/
typedef char wchar;
typedef char* pwchar;
class TString : public TArrayAllocation<wchar>
{
    //TODO: Crashed application - if usign is MakeObjectPool(TString);
    typedef TArrayAllocation<wchar> Base;
public:
    /*-------------------------------------------------------------------------
 | Iterator:
 | ------------
 | Helper for enumerable class
 ---------------------------------------------------------------------------*/
    class Iterator
    {
    public:
        Iterator(ref<TString> src, uint index)
            :  mSource(src), mIndex(index)
        {}

        bool operator !=(const Iterator& it)
        {
            return(mIndex != it.mIndex);
        }

        Iterator operator ++(void)
        {
			++mIndex;
            return *this;
        }

        const wchar operator*() const
        {
            return mSource->Chars()[mIndex];
        }

        const wchar operator->() const
        {
            return mSource->Chars()[mIndex];
        }

        const wchar& operator*()
        {
            return mSource->Chars()[mIndex];
        }

        const wchar& operator->()
        {
            return mSource->Chars()[mIndex];
        }
    private:
        ref<TString> mSource;
        uint mIndex;
    };

    enum { EndChar = 0 };
    TString(uint size = Capacity);
    TString(const pwchar copy);
    TString(const pwchar copy, uint start, uint end);
    TString(TString& copy);
    const pwchar				Chars() const;
    unsigned int				IndexOf(wchar symb) const;
    unsigned int				LastIndexOf(char symb, uint skip = 1) const;
    bool						Equal(const pwchar compare) const;
    bool						Equal(const pwchar compare, uint len) const;
public:
    void						Push(wchar symb);
    void						Push(const pwchar add, uint len);
    void						Push(const pwchar add);
    void						Clear(void);
    void						Assign(const pwchar copy);
    void						Endl(void);
    uint						GetHash(void) const
    {
        return mHash;
    }
public:
    inline Iterator Begin(void)
    {
        return Iterator(this, 0);
    }
    
	inline Iterator End(void)
    {
		return Iterator(this, Length());
    }
private:
    void CacheHash()
    {
        //reset
        mHash = 0;

        for(uint it = 0, end = Length(); it!=end; ++it)
        {
            char c = mBuffer[it];
            mHash ^= ((mHash << 5) + c + (mHash >> 2));
        }
    }
private:
    uint mHash;
};

/*-------------------------------------------------------------------------
| String:
| ------------
| String
---------------------------------------------------------------------------*/
class String : public ref<TString>
{
    typedef ref<TString> Base;
public:
	typedef TString::Iterator Iterator;

    String(void);
    String(uint dAlloc, bool);
    String(const pwchar copy);
    String(const pwchar copy, uint start, uint end);
    String(const String& copy);
    String(const String& copy, uint start, uint end);
    String(const String& copy, uint end);
    String(const ref<TString>& copy);

    // methods
    unsigned int SetLength(uint len);
    bool		 Equal(const String& comp) const;
    /* Operators */
    inline Base& operator = (const Base& copy) { return Base::operator=(copy); }
    inline Base& operator = (const ExNullptr& eq)  { return Base::operator=(eq); }

    void	operator =	(const pwchar copy);
    bool	operator == (const pwchar dCompare) const;
    bool	operator == (const String& dCompare) const;
    bool	operator != (const String& dCompare) const;
    String	operator +	(wchar dAdd) const;
    String	operator +	(const String& dAdd) const;
    String	operator +	(wchar dAdd);
    void	operator +=	(wchar dAdd);
    void	operator +=	(const String& add);
    const wchar	operator [] (int index) const;
    bool	operator > (const String& equal) const
    {
        return equal->GetHash() > mReference->GetHash();
    }
    bool	operator < (const String& equal) const
    {
        return equal->GetHash() < mReference->GetHash();
    }
protected:
    void Clear();
    void Push(char symb);
    void Push(const String& add);
private:
    //TODO: ADD THREAD SYNC
    inline TString * Unique(void)
    {
        if(!mReference->IsUnique())
        {
            Assign(new TString(*mReference));
        }
        return mReference;
    }
};

/*-------------------------------------------------------------------------
| Helper function(s):
| ------------
| TODO: MOVE IN CLASS
---------------------------------------------------------------------------*/
inline uint GetCStrLength(const pwchar copy)
{
    unsigned int retval = 0;
    for(; copy[retval] != 0; ++retval);
    return retval ;
}


inline bool CompareCStr(const pwchar one, const pwchar two, uint len)
{
    for(unsigned int i = 0; i != len; ++i)
        if(one[i] != two[i]) return false;
    return true;
}

inline uint FindLastOfCStr(const pwchar in, wchar symb, uint skip, uint len)
{
    for(uint i = len-1; i != Invalid; --i)
    {
        if((in[i] == symb)&& (--skip == 0))
            return i+1;
    }
    return Invalid;
}

/*-------------------------------------------------------------------------
| TString:
| ------------
| Implementation
---------------------------------------------------------------------------*/
inline TString::TString(uint size) : Base(),
    mHash(0)
{
    SetLength(size);
    mLength = 0;
    Endl();
}

inline TString::TString(const pwchar copy) : Base()
{
    uint allocSize = SetLength(GetCStrLength(copy));
    Move(copy, mBuffer, allocSize);
    Endl();
    CacheHash();
}

inline TString::TString(const pwchar copy, uint start, uint end) : Base()
{
    uint allocSize = SetLength((end-start));
    Move(copy + start, mBuffer, allocSize);
    Endl();
    CacheHash();
}

inline TString::TString(TString& copy) : Base()
{
    uint allocSize = SetLength(copy.Length());
    Move(copy.mBuffer, mBuffer, allocSize);
    Endl();
    CacheHash();
}

inline void TString::Assign(const pwchar copy)
{
    uint allocSize = SetLength(GetCStrLength(copy));
    Move(copy, mBuffer, allocSize);
    Endl();
    CacheHash();
}

inline void TString::Endl()
{
    mBuffer[mLength] = EndChar;
}

inline const pwchar TString::Chars() const 
{ 
    return mBuffer;
}

inline void TString::Push(wchar symb)
{
    SetLength(mLength+1);
    mBuffer[mLength-1] = symb;
    Endl();
    CacheHash();
}

inline void TString::Push(const pwchar add, uint len)
{
    SetLength(mLength+len);
    Move(add, (mBuffer+mLength)-len, len);
    Endl();
    CacheHash();
}

inline void TString::Push(const pwchar add)
{
    uint allocSize = SetLength(mLength+GetCStrLength(add));
    Move(add, mBuffer+mLength, allocSize);
    Endl();
    CacheHash();
}

inline uint TString::IndexOf(wchar) const
{
    return Invalid;
}

inline uint TString::LastIndexOf(wchar symb, uint skip/* = 1*/) const
{
    return FindLastOfCStr(mBuffer, symb, skip, mLength);
}

inline bool	TString::Equal(const pwchar comp) const
{
    return CompareCStr(comp, Chars(), Math::Min(GetCStrLength(comp), mLength));
}

inline bool	TString::Equal(const pwchar comp, uint len) const
{
    return CompareCStr(comp, Chars(), Math::Min(len, mLength));
}

inline void TString::Clear()
{
    mLength = 0;
    Endl();
    CacheHash();
}

/*-------------------------------------------------------------------------
| String:
| ------------
| String
---------------------------------------------------------------------------*/
inline String::String(uint alloc, bool)
    : Base(new TString(alloc))
{
}

inline String::String()
    : Base(new TString())
{

}

inline String::String(const pwchar copy)
    : Base(new TString(copy))
{

}

inline String::String(const pwchar copy, uint start, uint end)
    : Base(new TString(copy, start, end))
{

}

inline String::String(const String& copy) 
    : Base(copy.mReference)
{

}

inline String::String(const String& copy, uint start, uint end) 
    : Base(new TString(copy->Chars(), start, end))
{

}

inline String::String(const String& copy, uint end) 
    : Base(new TString(copy->Chars(), 0, end))
{
}

inline String::String(const ref<TString>& copy)
    : Base(copy)
{

}

inline void	String::Clear()
{
    Unique()->Clear();
}

inline void	String::Push(wchar symb)
{
    Unique()->Push(symb);
}

inline void String::Push(const String& add)
{
    if(mReference->Empty())
    {
        Assign(add);
    }
    else
    {
        Unique()->Push(add->Chars(), add->Length());
    }
}

inline unsigned int String::SetLength(uint len)
{
    return Unique()->SetLength(len);
}

inline bool String::Equal(const String& comp) const
{
    if(Base::Equal(comp)) return true;

    // compare length
    if(comp->Length() != mReference->Length())
    {
        return false;
    }

    //compare hash
    if(mReference->GetHash() != comp->GetHash())
    {
        return false;
    }

    //compare text
    return mReference->Equal(comp->Chars(), comp->Length());
}

/*
* Operators
*/
inline void String::operator = (const pwchar copy)
{
    Unique()->Assign(copy);
}

inline bool String::operator == (const pwchar comp) const
{
    //comp - to Hash?
    return mReference->Equal(comp);
}

inline bool String::operator == (const String& comp) const
{
    return Equal(comp);
}

inline bool String::operator != (const String& comp) const
{
    return !Equal(comp);
}

inline String String::operator + (wchar add) const
{
    String result = mReference->Chars();
    result->Push(add);
    return result;
}

inline String String::operator + (wchar add)
{
    String result = mReference->Chars();
    result->Push(add);
    return result;
}

inline String String::operator + (const String& add) const
{
    String result = mReference->Chars();
    result.Push(add);
    return result;
}

inline void String::operator += (wchar add)
{
    Unique()->Push(add);
}

inline void String::operator += (const String& add)
{
    Push(add);
}

inline const wchar String::operator [] (int index) const
{
    //TODO: check index
    return mReference->Chars()[index];
}

